/*
 ╔═════════════════════════════════════════════════════════════╗
 ║   DialogBox 对话框系统 (DialogBox.cs)                         ║
 ║                                                              ║
 ║   功能：                                                  ║
 ║     1. 根据配置的角色位置列表（SliceConfig[]），在场景中  ║
 ║        动态播放角色进入动画（移动 + 透明度渐变）。         ║
 ║     2. 提供单角色入场完成和全部角色入场完成的事件回调。   ║
 ║     3. 入场动画完成后自动启动打字机效果并按顺序播放对话。   ║
 ║                                                            ║
 ║   使用方法：                                              ║
 ║     1. 将本脚本挂载到场景中的空 GameObject 上。             ║
 ║     2. 在 Inspector 中配置 SliceConfigs 数组：             ║
 ║        - Position：枚举值，表示角色在左/中/右位置。        ║
 ║        - SliceObject：对应的角色 GameObject。             ║
 ║        - EntryOffset：自定义出场偏移。若为(0,0,0)，将    ║
 ║          自动根据 Position 设置屏幕外偏移。              ║
 ║        - EntryDuration：出场动画时长（秒）。             ║
 ║        - MoveEase：DOTween 缓动曲线。                     ║
 ║     3. 将 TypewriterEffectPrefab 和 ClickPropagationHandler ║
 ║        拖入对应字段。                                     ║
 ║     4. 通过代码订阅以下事件：                             ║
 ║        OnSliceEntryComplete(DialogueRolePosition pos)     ║
 ║        OnAllSlicesEntryComplete()                         ║
 ║     5. 事件触发后可自行控制逻辑，全部入场完成自动开始对话。 ║
 ║                                                            ║
 ║   注意事项：                                              ║
 ║     - EntryOffset 和 MoveEase 可按需求自行调整。          ║
 ║     - 若 SliceConfigs 数组为空，则会立即开始对话。       ║
 ╚════════════════════════════════════════════════════════════╝
 */
using System;
using System.Collections.Generic;
using System.IO;
using DG.Tweening;
using UnityEngine;
using ZhanQi.EventGraph.Models;

public class DialogBox : MonoBehaviour
{
    // 单个角色入场完成事件，参数为角色位置枚举
    public event Action<DialogueRolePosition> OnSliceEntryComplete;
    // 全部角色入场完成事件
    public event Action OnAllSlicesEntryComplete;
    // 配置每个角色的出场动画参数
    [Serializable]
    public class SliceConfig {
        // 角色在对话框中的位置（左/中/右）
        public DialogueRolePosition Position;
        // 对应的角色 GameObject
        public GameObject SliceObject;
        // 从屏幕外开始的偏移位移，若为 (0,0,0) 则自动根据位置计算
        public Vector3 EntryOffset;                          // offset to start off-screen
        // 出场动画持续时间（秒）
        public float EntryDuration = 0.5f;                   // how long the entry tween lasts
        // 出场动画的缓动曲线
        public Ease MoveEase = Ease.OutQuad;                 // easing for movement
    }

    // 打字机效果预制体，用于逐字显示对话
    public TypewriterEffect TypewriterEffectPrefab;
    // 点击事件处理器，用于等待玩家点击
    public ClickPropagationHandler ClickPropagationHandler;
    // 要播放的对话文件名（不含后缀）
    public string FileName = "";

    // 角色出场配置列表
    public SliceConfig[] sliceConfigs;

    // 保存每个角色的原始本地位置
    private Dictionary<DialogueRolePosition, Vector3> originalPositions = new Dictionary<DialogueRolePosition, Vector3>();

    // 对话 JSON 文件所在目录
    private const string saveDirectory = "Assets/PlayerData/EventDatat";

    // 方法：Start — 缓存位置，播放入场动画并注册对话启动
    private void Start()
    {
        CacheOriginalPositions();
        OnAllSlicesEntryComplete += () => StartTyping(FileName);
        PlayEntryAnimations();
    }

    // 方法：StartTyping — 异步读取 JSON 并按节点逐句播放对话
    public async void StartTyping(string _FileName)
    {
        FileName = _FileName;
        var path = Path.Combine(saveDirectory, FileName + ".json");
        var json = File.ReadAllText(path);
        EventGraphData graphData = JsonUtility.FromJson<EventGraphData>(json) ?? new EventGraphData();
        foreach (DialogueItem dialogueItem in graphData.nodes[0].dialogueItems)
        {
            RoleStatusUpdate(dialogueItem.MainInterviewer);

            TypewriterEffectPrefab.StartTyping(dialogueItem.Value);
            await ClickPropagationHandler.onClick.WaitForInvoke();
            if (!TypewriterEffectPrefab.isEnd)
            {
                TypewriterEffectPrefab.CompleteTyping();
                await ClickPropagationHandler.onClick.WaitForInvoke();
            }
        }
    }

    // 方法：UpdateSliceTint — 根据当前发言者更新角色高亮颜色
    private void UpdateSliceTint(SpineGlobalTint spine, DialogueRolePosition mainInterviewer, DialogueRolePosition selfPosition)
    {
        Color target = (mainInterviewer == selfPosition) ? Color.white : Color.gray;
        DOTween.To(
            () => spine.tint,
            x => { spine.tint = x; spine.ApplyTint(); },
            target,
            0.3f
        );
    }

    // 方法：RoleStatusUpdate — 遍历配置，为每个角色调用 UpdateSliceTint
    private void RoleStatusUpdate(DialogueRolePosition mainInterviewer)
    {
        foreach (var config in sliceConfigs) {
            if (config.SliceObject == null) continue;
            if (config.SliceObject.TryGetComponent<SpineGlobalTint>(out var spine)) {
                UpdateSliceTint(spine, mainInterviewer, config.Position);
            }
        }
    }

    // 方法：CacheOriginalPositions — 缓存原始位置并将角色移出屏幕外，同时设置透明度为 0
    private void CacheOriginalPositions() {
        foreach (var config in sliceConfigs) {
            if (config.SliceObject == null) continue;
            var tr = config.SliceObject.transform;
            originalPositions[config.Position] = tr.localPosition;
            // Determine default off-screen entry offset if none provided
            Vector3 offset = config.EntryOffset;
            if (offset == Vector3.zero) {
                switch (config.Position) {
                    case DialogueRolePosition.Left:
                        offset = new Vector3(-Screen.width, 0, 0);
                        break;
                    case DialogueRolePosition.Right:
                        offset = new Vector3(Screen.width, 0, 0);
                        break;
                    case DialogueRolePosition.Middle:
                        offset = new Vector3(0, Screen.height, 0);
                        break;
                }
            }
            tr.localPosition += offset;
            if (config.SliceObject.TryGetComponent<SpineGlobalTint>(out var spine)) {
                var c = spine.tint;
                spine.tint = new Color(c.r, c.g, c.b, 0f);
                spine.ApplyTint();
            }
        }
    }

    // 方法：PlayEntryAnimations — 播放所有角色的移动和透明度 Tween，并触发对应事件
    private void PlayEntryAnimations() {
        int total = sliceConfigs.Length;
        int completed = 0;

        foreach (var config in sliceConfigs) {
            if (config.SliceObject == null) {
                total--;
                continue;
            }

            var tr = config.SliceObject.transform;
            var targetPos = originalPositions[config.Position];

            // Move tween
            var moveTween = tr.DOLocalMove(targetPos, config.EntryDuration).SetEase(config.MoveEase);

            // Color tween if present
            Tween colorTween = null;
            if (config.SliceObject.TryGetComponent<SpineGlobalTint>(out var spine)) {
                var originalTint = new Color(spine.tint.r, spine.tint.g, spine.tint.b, 1f);
                colorTween = DOTween.To(
                    () => spine.tint,
                    x => { spine.tint = x; spine.ApplyTint(); },
                    originalTint,
                    config.EntryDuration
                ).SetEase(config.MoveEase);
            }

            // When move completes, fire individual event
            moveTween.OnComplete(() => {
                OnSliceEntryComplete?.Invoke(config.Position);
                completed++;
                if (completed >= total) {
                    OnAllSlicesEntryComplete?.Invoke();
                }
            });
        }

        // If no valid slices, immediately fire all complete
        if (total == 0) {
            OnAllSlicesEntryComplete?.Invoke();
        }
    }
}